// File [test/pointers/auto_new_ptr/01/main.cpp]

#include <iostream>
#include <ostream>
#include <alfs/pointers/AutoNewPtr.hpp>

#include    <string>
#include    <boost/function.hpp>

class Base {};
class Derived: public Base {};

int testAutoDestroy_nDestroyCalls = 0;
namespace std
{
    template<>
    struct default_delete< alfs::DestructionEventWrapper<Derived> >
    {
        void operator()( alfs::DestructionEventWrapper<Derived>* p ) const
        {
            std::cout << "Destroyed (standard)" << std::endl;
            delete p;
            ++testAutoDestroy_nDestroyCalls;
        }
    };
}  // std

void testAutoDestroy()
{
    using namespace alfs;
    std::cout << "\n* testAutoDestroy:" << std::endl;
    {
        AutoNewPtr<Derived>    p( newNoArgs );
        AutoNewPtr<Base>       q = p;
#ifdef  SHOULD_FAIL_DERIVED_FROM_BASE
        AutoNewPtr<Derived>    r = q;      // Should not compile.
#endif
    }
    (testAutoDestroy_nDestroyCalls == 1) || throwX( "Failed, destroy count != 1" );
    std::cout << "Passed testAutoDestroy" << std::endl;
}

class Naughty
{
public:
    virtual ~Naughty() {}
    double pi() const { return 3.14; }
    void selfDestroy() { delete this; }
};

void testAccessCheck()
{
    using namespace alfs;
    std::cout << "\n* testAccessCheck:" << std::endl;
    bool stdExceptionExpected = false;
    try
    {
        AutoNewPtr<Naughty>    p( newNoArgs );

        p->pi();
        std::cout << "Good, access of valid pointer succeeded" << std::endl;
        p->selfDestroy();
        stdExceptionExpected = true;  p->pi();
        throw 666;
    }
    catch( int )
    {
        throwX( "Failed testAccessCheck" );
    }
    catch( std::exception const& x)
    {
        if( !stdExceptionExpected ) { throw; }
        std::cout << "Good, access of invalid pointer failed, \"" << x.what() << "\"" << std::endl;
    }
    std::cout << "Passed testAccessCheck" << std::endl;
}


class NumberHolder
{
private:
    double  myValue;
public:
    NumberHolder( double v ): myValue( v ) {}
    double value() const { return myValue; }
};

#   define ThisClass    UserCommand

    class ThisClass
    {
    public:
        enum Enum { null = 0, maxValue = INT_MAX };
        // The implementation (e.g. wxWidgets) may not support larger id range than int.

    private:
        Enum            myId;
        std::wstring    myDefaultText;

        static Enum newId()
        {
            static Enum     lastGeneratedId = null;

            lastGeneratedId = Enum( lastGeneratedId - 1 );
            return lastGeneratedId;
        }

        void assertInvariant()
        {
            assert( !(myId != null && myDefaultText.length() == 0) );
        }

    protected:
        virtual ~ThisClass() {}

    public:
        typedef alfs::AutoNewPtr<ThisClass>   Ptr;

        ThisClass()
            : myId( null ), myDefaultText()
        {
            assertInvariant();
        }

        ThisClass( Enum id, std::wstring const& defaultText )
            : myId( id ), myDefaultText( defaultText )
        {
            assertInvariant();
        }

        ThisClass( std::wstring const& defaultText )
            : myId( newId() ), myDefaultText( defaultText )
        {
            assertInvariant();
        }

        Enum id() const             { return myId; }
        std::wstring text() const   { return myDefaultText; }

        bool operator<( UserCommand const& other ) const
        { return id() < other.id(); }

        bool operator==( UserCommand const& other ) const
        { return id() == other.id(); }
    };
#   undef   ThisClass


#   define  ThisClass   InvocableUserCommand

    class ThisClass
        : public UserCommand
    {
    public:
        typedef boost::function<void()>     Action;

        static void noAction() {}

    private:
        typedef UserCommand     BaseClass;
        Action      myAction;

    protected:
    public:
        virtual ~ThisClass() {}

    public:
        typedef alfs::AutoNewPtr<ThisClass>   Ptr;

        ThisClass( Enum id, std::wstring const& defaultText, Action const& action )
            : BaseClass( id, defaultText )
            , myAction( action )
        {}

        ThisClass( Enum id, std::wstring const& s )
            : BaseClass( id, s )
            , myAction( noAction )
        {}
    };
#   undef   ThisClass

void foo( InvocableUserCommand::Ptr const& ) {}


class TroublesomeArg
{
public:
    typedef boost::function<void()>     Action;
    static void noAction() {}

    typedef alfs::AutoNewPtr<TroublesomeArg>    Ptr;

    TroublesomeArg() {}
    TroublesomeArg( Action const& ) {}
};

void testConstructorArg()
{
    using namespace alfs;
    std::cout << "\n* testConstructorArg:" << std::endl;
    {
        AutoNewPtr<NumberHolder>    p( 3.14 );
        std::cout << "Value = " << p->value() << std::endl;

        //foo( InvocableUserCommand::Ptr(
        //    InvocableUserCommand::Enum(0), L"asdf" //, InvocableUserCommand::noAction
        //    ) );
        TroublesomeArg::Ptr     d( &TroublesomeArg::noAction, new1Arg );
    }
    std::cout << "Passed testConstructorArg" << std::endl;
}

class Simple {};

void testConversionToCheckingPtr()
{
    using namespace alfs;
    std::cout << "\n* testConversionToCheckingPtr:" << std::endl;
    {
        AutoNewPtr<Simple>     p( newNoArgs );
        CheckingPtr< DestructionEventWrapper<Simple> >    q = p.asCheckingPtr();
    }
    std::cout << "Passed testConversionToCheckingPtr" << std::endl;
}

class PolymorphicA { public: virtual ~PolymorphicA() {} };
class PolymorphicB { public: virtual ~PolymorphicB() {} };
class Polymorphic: public PolymorphicA, public PolymorphicB {};

class PolymorphicUnrelated { public: ~PolymorphicUnrelated() {} };

void testDynamicConversion()
{
    using namespace alfs;
    std::cout << "\n* testDynamicConversion:" << std::endl;
    {
        AutoNewPtr<Polymorphic>     p( newNoArgs );
        AutoNewPtr<PolymorphicA>    a = p;

        AutoNewPtr<PolymorphicB>    b = a.convertedOrVoid();
        (b.hasValidReferent()) || throwX( "Failed dynamic conversion (init) to related type" );

        b = a.convertedOrVoid();
        (b.hasValidReferent()) || throwX( "Failed dynamic conversion (asgn) to related type" );

        AutoNewPtr<PolymorphicUnrelated>    u = a.convertedOrVoid();
        (u.isVoid()) || throwX( "Incorrectly succeeded dynamic conversion (init) to unrelated type" );

        u = a.convertedOrVoid();
        (u.isVoid()) || throwX( "Incorrectly succeeded dynamic conversion (asgn) to unrelated type" );
    }
    std::cout << "Passed testDynamicConversion" << std::endl;
}

int main()
{
    static char const testName[] = "AutoNewPtr test 01";
    using namespace alfs;
    std::cout << testName << std::endl;
    try
    {
        testAutoDestroy();
        testAccessCheck();
        testConstructorArg();
        testConversionToCheckingPtr();
        testDynamicConversion();

        std::cout << "\n" << testName << " Passed all tests." << std::endl;
        return EXIT_SUCCESS;
    }
    catch( std::exception const& x )
    {
        std::cout << testName << " Failed" << std::endl;
        std::cerr << "!" << x.what() << std::endl;
        return EXIT_FAILURE;
    }
}
